package com.stone.inventory.sharding.config;

import com.stone.inventory.sharding.config.properties.DbShardingProperty;
import com.stone.inventory.sharding.config.properties.GlobalIdProperties;
import com.stone.inventory.sharding.config.properties.InvRedisProperty;
import com.stone.inventory.sharding.config.resolver.ShardingResolver;
import com.stone.inventory.sharding.helper.ShardingHelper;
import com.stone.inventory.sharding.id.ShardingIdInjector;
import com.stone.inventory.sharding.id.TableIdGenerator;
import com.stone.inventory.sharding.infra.algorithm.ShardingAlgorithm;
import com.stone.starter.redis.client.RedisShardingClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.ArrayList;
import java.util.List;

/**
 * sharding分片自动配置
 *
 * @author Mr_wenpan@163.com 2021/08/11 10:56
 */
@Configuration
@EnableConfigurationProperties({
        DbShardingProperty.class,
        InvRedisProperty.class
})
public class StoneShardingAutoConfiguration {

    @Value("${server.port:8080}")
    private Integer port;

    @Value("${spring.cloud.client.ip-address:default}")
    private String serverHost;

    /**
     * 全局分布式id配置
     */
    @Bean
    public GlobalIdProperties globalIdProperties(DbShardingProperty dbShardingProperty) {

        GlobalIdProperties globalIdProperties = dbShardingProperty.getShardingIdRule();
        if (null == dbShardingProperty.getInstanceId()) {
            // 获取节点ID的最大值
            long nodeIdMask = ~(-1L << globalIdProperties.getNodeBits());
            // 主机ip + 端口 求hash码，这种方式得到的hash值也有可能重复
            int hashCode = Math.abs((port + serverHost).hashCode());
            dbShardingProperty.setInstanceId(hashCode % nodeIdMask);
        }
        globalIdProperties.setInstanceId(dbShardingProperty.getInstanceId());
        return globalIdProperties;
    }

    /**
     * 表ID生成器
     */
    @Bean
    public List<TableIdGenerator> tableIdGenerators(GlobalIdProperties globalIdProperties, DbShardingProperty dbShardingProperty) {
        dbShardingProperty.setShardingIdRule(globalIdProperties);
        return ShardingResolver.parseTableIdGenerators(dbShardingProperty);
    }

    /**
     * 分片算法
     */
    @Bean
    @ConditionalOnMissingBean(ShardingAlgorithm.class)
    public List<ShardingAlgorithm> shardingAlgorithms() {
        return new ArrayList<>();
    }

    /**
     * 分片id注入者
     */
    @Bean
    @ConditionalOnMissingBean(ShardingIdInjector.class)
    public List<ShardingIdInjector> shardingIdInjectors() {
        return new ArrayList<>();
    }

    /**
     * 注意：这里的库存分片Redis配置不能和redisSharding分片的Redis实例重复
     *
     * @param invRedisProperty    分片属性配置
     * @param dbShardingProperty  库存分片整体配置
     * @param tableIdGenerators   表Id生成器
     * @param shardingAlgorithms  分片算法
     * @param shardingIdInjectors 分片id注入器
     * @param redisShardingClient Redis分片客户端
     * @return com.stone.inventory.sharding.helper.ShardingHelper
     * @author Mr_wenpan@163.com 2021/8/16 9:32 下午
     */
    @Bean
    public ShardingHelper shardingHelper(InvRedisProperty invRedisProperty,
                                         DbShardingProperty dbShardingProperty,
                                         List<TableIdGenerator> tableIdGenerators,
                                         List<ShardingAlgorithm> shardingAlgorithms,
                                         List<ShardingIdInjector> shardingIdInjectors,
                                         RedisShardingClient redisShardingClient) {
        // db 分片配置
        ShardingHelper shardingHelper = new ShardingHelper(dbShardingProperty.isSharding());
        shardingHelper.setSchema(dbShardingProperty.getSchema());
        for (TableIdGenerator tableIdGenerator : tableIdGenerators) {
            // 表id生成器
            shardingHelper.register(tableIdGenerator);
        }
        for (ShardingAlgorithm shardingAlgorithm : shardingAlgorithms) {
            // 注册分片算法
            shardingHelper.register(shardingAlgorithm);
        }
        for (ShardingIdInjector shardingIdInjector : shardingIdInjectors) {
            // 注册ID注入器
            shardingHelper.register(shardingIdInjector);
        }

        // redis分片配置
        shardingHelper.setTableRules(dbShardingProperty.getTableRules());
        // 解析库存分片Redis配置（注意配置和Redissharding分片客户端配置重复问题）
        ShardingResolver.parseRedisConnect(invRedisProperty, redisShardingClient);
        shardingHelper.setRedisShardingClient(redisShardingClient);
        shardingHelper.setDefaultRedisInstance(invRedisProperty.getDefaultInstance());
        shardingHelper.setRedisShardingMapping(invRedisProperty.getShardingMapping());
        return shardingHelper;
    }
}
